const EventEmitter = require('events')
const chalk = require('chalk')
const consoleUtil = require('./util/consoleUtil')
const execa = require('execa')
const path = require('path')
const spinner = require('./spinner')
const fs = require('fs-extra')
const merge = require('webpack-merge')
const generateReadme = require('./util/generateReadme')
const generateHtml = require('./util/generateHtml')
const mirrors = require('./util/mirrors')

module.exports = class Creator extends EventEmitter {
  constructor(name, context) {
    super()
    this.name = name
    this.context = context
  }

  async create(options) {
    console.log('start generate project')
    let cli
    if (options.cliUtil === 'vue') {
      cli = '@vue/cli create -d'
    } else if (options.cliUtil === 'react') {
      cli = 'create-react-app'
    }

    const projectPkgPath = path.resolve(this.context, 'package.json')

    if (cli) {
      consoleUtil.clear()
      console.log(`use ${chalk.yellowBright(options.cliUtil)} official ${chalk.yellowBright('cli')} to initialize project!`)
      console.log('run: ' + chalk.yellowBright(`npx ${cli} ${options.appName}`))
      spinner.startSpinner('generating..., it will take a few minutes')
      try {
        await this.run(`npx ${cli} ${options.appName}`, null, path.resolve(this.context, '../'))
        spinner.successSpinner(chalk.greenBright('generate project succeed'))
      } catch (err) {
        spinner.failSpinner(chalk.redBright('generate project failed'))
        throw err
      }
    } else {
      fs.ensureDirSync(this.context)
      fs.writeJSONSync(projectPkgPath, {
        "name": this.name,
        "private": true,
        "version": "0.1.0",
        "dependencies": {},
        "devDependencies": {
          "webpack": "^4.42.0",
          "webpack-dev-server": "^3.10.3",
          "@babel/core": "^7.8.3",
          "@babel/preset-env": "^7.8.3",
          "babel-loader": "^8.0.6",
          "clean-webpack-plugin": "^3.0.0",
          "html-webpack-plugin": "^3.2.0",
          "style-loader": "^1.1.3",
          "css-loader": "^3.4.2"
        },
        "scripts": {
          "start": "webpack-dev-server",
          "build": "webpack"
        },
      }, {spaces:2})
      // initialize public dir
      fs.ensureDirSync(path.resolve(this.context, 'public'))
      fs.writeFileSync(
        path.resolve(this.context, 'public/index.html'),
        generateHtml.generate(),
        'utf8'
      )
      // initialize src dir
      fs.copySync(
        path.resolve(__dirname, '../template/src'), 
        path.resolve(this.context, 'src'), 
        {overwrite: true}
      )
      fs.copySync(
        path.resolve(__dirname, '../template/webpack.config.js'), 
        path.resolve(this.context, 'webpack.config.js'), 
        {overwrite: true}
      )
      fs.writeFileSync(
        path.resolve(this.context, '.gitignore'),
        '# dependencies\n'+
        '/node_modules\n\n'+
        'npm-debug.log*',
        'utf8'
      )
      // initialize git 
      try {
        await this.run('git init')
        await this.run('git add .')
        await this.run('git',  ['commit', '-m', 'initial'])
      } catch (err) {
        spinner.failSpinner(chalk.redBright('generate project failed'))
        throw err
      }
      
    }
    spinner.startSpinner(`updating ${chalk.blueBright('package.json')}`)
    fs.removeSync(path.resolve(this.context, 'yarn.lock'))
    fs.removeSync(path.resolve(this.context, 'package-lock.json'))
    
    const packageJson = fs.readJsonSync(projectPkgPath)
    packageJson.devDependencies = {...packageJson.devDependencies, ...packageJson.dependencies}
    delete packageJson.dependencies

    const defaultPkg = require('../template/defaultPackage.json')
    const finalPkgJson = merge(packageJson, defaultPkg)

    finalPkgJson.build = {
      ...finalPkgJson.build,
      appId: `com.example.${finalPkgJson.name}`,
      productName: finalPkgJson.name,
      copyright: `Copyright © ${new Date().getFullYear()} \${author}`,
    }
  
    if (options.cliUtil === 'vue') {
      finalPkgJson["vue"] = {
        devServer: { port: 3000 },
        outputDir: "build"
      }
      finalPkgJson.scripts.dev = 
        `concurrently \"wait-on http://localhost:3000 && electron .\" \"cross-env BROWSER=none npm run serve\" `
    }
    fs.writeJSONSync(projectPkgPath, finalPkgJson, {spaces: 2})

    spinner.startSpinner(`generate electron ${chalk.blueBright('assets')}`)
    fs.copySync(
      path.resolve(__dirname, '../template/main.js'), 
      path.resolve(this.context, 'main.js'), 
      {overwrite: true}
    )
    fs.copySync(
      path.resolve(__dirname, '../template/assets'), 
      path.resolve(this.context, 'assets'), 
      {overwrite: true}
    )
    fs.copySync(
      path.resolve(__dirname, '../template/webpack.electron.config.js'), 
      path.resolve(this.context, 'webpack.electron.config.js'), 
      {overwrite: true}
    ) 

    spinner.startSpinner('updating README.md')
    fs.writeFileSync(
      path.resolve(this.context, 'README.md'),
      generateReadme.generate(options.cliUtil),
      'utf8'
    )

    spinner.startSpinner('updating .gitignore')
    fs.appendFileSync(
      path.resolve(this.context, '.gitignore'),
      `\n\n\n#electron dist\n`+
      `/dist\n`+
      `/build\n`,
      'utf8'
    )

    // set registry
    process.env.npm_config_registry = mirrors.taobao
    process.env.npm_config_ELECTRON_MIRROR = mirrors.electron_mirror
    process.env.npm_config_electron_custom_dir 
      = finalPkgJson.devDependencies.electron
    
    spinner.stopSpinner()
    console.log('install dependencies, it will take a few minutes')
    try {
      await this.run('npm install', null, null, 'inherit')

      spinner.startSpinner('updating git repo')
      await this.run('git add .')
      let msg = 'add electron deps'
      if (options.git) {
        msg = options.git
      }
      await this.run(`git`, ['commit', '-m', msg])

      spinner.stopSpinner()
      console.log(`(●'◡'●) `, ` Successfully created project ${chalk.cyan(this.name)}.`)
    } catch (err) {
      throw err
    }
  
  }

  run(command, args, context, stdio='pipe') {
    if (!args) { 
      [command, ...args] = command.split(/\s+/) 
    }
    const subprocess = execa(command, args, { 
      cwd: context || this.context,
      stdio
    })
    return subprocess
  }
} 